from datetime import datetime, timedelta
from typing import Optional
import bcrypt
from jose import JWTError, jwt
from fastapi import Depends, HTTPException, status
from fastapi.requests import Request
from fastapi.responses import RedirectResponse
from sqlalchemy.orm import Session
from ..config import SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES, DEFAULT_USERNAME, DEFAULT_PASSWORD
from ..models import get_db, User
from ..schemas import TokenData

# 验证密码
def verify_password(plain_password: str, hashed_password: str) -> bool:
    """
    验证明文密码和哈希密码是否匹配
    
    Args:
        plain_password: 明文密码
        hashed_password: 哈希密码
    
    Returns:
        密码是否匹配
    """
    # bcrypt限制密码长度不超过72字节，需要截断
    if len(plain_password.encode()) > 72:
        plain_password = plain_password.encode()[:72]
    else:
        plain_password = plain_password.encode()
    
    # 确保哈希密码是bytes类型
    if isinstance(hashed_password, str):
        hashed_password = hashed_password.encode()
    
    try:
        return bcrypt.checkpw(plain_password, hashed_password)
    except Exception:
        return False

# 获取密码哈希
def get_password_hash(password: str) -> str:
    """
    生成密码的哈希值
    
    Args:
        password: 明文密码
    
    Returns:
        哈希后的密码
    """
    # bcrypt限制密码长度不超过72字节，需要截断
    if len(password.encode()) > 72:
        password = password.encode()[:72]
    else:
        password = password.encode()
    
    # 生成盐并哈希密码
    salt = bcrypt.gensalt(rounds=12)
    hashed = bcrypt.hashpw(password, salt)
    return hashed.decode()

# 获取用户
def get_user(db: Session, username: str) -> Optional[User]:
    """
    根据用户名获取用户
    
    Args:
        db: 数据库会话
        username: 用户名
    
    Returns:
        用户对象或None
    """
    return db.query(User).filter(User.username == username).first()

# 认证用户
def authenticate_user(db: Session, username: str, password: str) -> Optional[User]:
    """
    认证用户
    
    Args:
        db: 数据库会话
        username: 用户名
        password: 密码
    
    Returns:
        认证成功返回用户对象，失败返回False
    """
    user = get_user(db, username)
    if not user:
        return False
    if not verify_password(password, user.password_hash):
        return False
    return user

# 创建访问令牌
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
    """
    创建JWT访问令牌
    
    Args:
        data: 要编码的数据
        expires_delta: 过期时间增量
    
    Returns:
        编码后的JWT令牌
    """
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

# 修改用户密码
def update_user_password(db: Session, user: User, old_password: str, new_password: str) -> bool:
    """
    修改用户密码
    
    Args:
        db: 数据库会话
        user: 用户对象
        old_password: 旧密码
        new_password: 新密码
    
    Returns:
        是否修改成功
    """
    # 验证旧密码是否正确
    if not verify_password(old_password, user.password_hash):
        return False
    
    # 更新密码哈希
    user.password_hash = get_password_hash(new_password)
    db.commit()
    return True

# 获取当前用户
async def get_current_user(request: Request, db: Session = Depends(get_db)) -> User:
    """
    获取当前登录用户
    
    Args:
        request: 请求对象
        db: 数据库会话
    
    Returns:
        当前用户对象
    
    Raises:
        HTTPException: 如果认证失败，会抛出异常并触发重定向到登录页面
    """
    token = request.cookies.get("access_token")
    if token is None:
        # 抛出异常并重定向到登录页面
        raise HTTPException(
            status_code=status.HTTP_302_FOUND,
            detail="未找到访问令牌",
            headers={"Location": f"/admin/login?redirect={request.url.path}"}
        )
    
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            # 令牌无效，抛出异常并重定向
            raise HTTPException(
                status_code=status.HTTP_302_FOUND,
                detail="无效的访问令牌",
                headers={"Location": f"/admin/login?redirect={request.url.path}"}
            )
        token_data = TokenData(username=username)
    except JWTError:
        # JWT错误，抛出异常并重定向
        raise HTTPException(
            status_code=status.HTTP_302_FOUND,
            detail="令牌验证失败",
            headers={"Location": f"/admin/login?redirect={request.url.path}"}
        )
    
    user = get_user(db, username=token_data.username)
    if user is None:
        # 用户不存在，抛出异常并重定向
        raise HTTPException(
            status_code=status.HTTP_302_FOUND,
            detail="用户不存在",
            headers={"Location": f"/admin/login?redirect={request.url.path}"}
        )
    
    return user

# 创建初始管理员用户
def create_initial_user():
    """
    创建初始管理员用户（如果不存在）
    """
    db = SessionLocal()
    try:
        # 检查是否已有用户
        user = db.query(User).first()
        if not user:
            # 创建默认管理员用户
            admin_user = User(
                username=DEFAULT_USERNAME,
                password_hash=get_password_hash(DEFAULT_PASSWORD)
            )
            db.add(admin_user)
            db.commit()
            print(f"初始管理员用户创建成功: 用户名={DEFAULT_USERNAME}, 密码={DEFAULT_PASSWORD}")
    except Exception as e:
        print(f"创建初始用户失败: {str(e)}")
        db.rollback()
    finally:
        db.close()

# 从models模块导入SessionLocal
from ..models import SessionLocal